package com.belly.template;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.belly.model.Meta;
import com.belly.model.enums.FileGenerateTypeEnum;
import com.belly.model.enums.FileTypeEnum;
import com.belly.template.model.TemplateMakerConfig;
import com.belly.template.model.TemplateMakerFileConfig;
import com.belly.template.model.TemplateMakerModelConfig;
import com.belly.template.model.TemplateMakerOutputConfig;

import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Belly
 * @version 1.1.0
 */
public class TemplateMaker {

    public static long MakerTemplate(TemplateMakerConfig templateMakerConfig) {
        Long id = templateMakerConfig.getId();
        Meta meta = templateMakerConfig.getMeta();
        String originProjectPath = templateMakerConfig.getOriginProjectPath();
        TemplateMakerFileConfig templateMakerFileConfig = templateMakerConfig.getTemplateMakerFileConfig();
        TemplateMakerModelConfig templateMakerModelConfig = templateMakerConfig.getTemplateMakerModelConfig();
        TemplateMakerOutputConfig templateMakerOutputConfig = templateMakerConfig.getTemplateMakerOutputConfig();

        return makeTemplate(meta, originProjectPath, templateMakerFileConfig, templateMakerModelConfig, templateMakerOutputConfig, id);
    }

    /**
     * 生成ftl模板和数据模板工具
     *
     * @param meta
     * @param originProjectPath
     * @param templateMakerFileConfig
     * @param templateMakerModelConfig
     * @param templateMakerOutputConfig
     * @param id
     * @return
     */
    public static long makeTemplate(Meta meta, String originProjectPath,
                                    TemplateMakerFileConfig templateMakerFileConfig,
                                    TemplateMakerModelConfig templateMakerModelConfig,
                                    TemplateMakerOutputConfig templateMakerOutputConfig,
                                    Long id) {
        if (null == id) {
            id = IdUtil.getSnowflakeNextId();
        }

        //路径信息
        String projectPath = System.getProperty("user.dir");
        String tempDirPath = projectPath + File.separator + ".temp";
        String templatePath = tempDirPath + File.separator + id;

        //判断是否为首次制作模板
        if (!FileUtil.exist(templatePath)) {
            FileUtil.mkdir(templatePath);
            FileUtil.copy(originProjectPath, templatePath, true);
        }

        //获取输入文件绝对路径
//        String sourceRootPath = templatePath + File.separator
//                + FileUtil.getLastPathEle(Paths.get(originProjectPath)).toString();
        String sourceRootPath = FileUtil.loopFiles(new File(templatePath), 1, null)
                .stream()
                .filter(File::isDirectory)
                .findFirst()
                .orElseThrow(RuntimeException::new)
                .getAbsolutePath();
        sourceRootPath = sourceRootPath.replaceAll("\\\\", "/");

        List<TemplateMakerFileConfig.FileInfoConfig> fileInfoConfigList
                = templateMakerFileConfig.getFiles();

        List<Meta.FileConfig.FileInfo> fileInfoList = makerFileTemplates(templateMakerFileConfig, templateMakerModelConfig, sourceRootPath);

        ArrayList<Meta.ModelConfig.ModelInfo> newModelInfoList = getModelInfoList(templateMakerModelConfig);


        //生成meta.json配置文件
        String metaOutputPath = templatePath + File.separator + "meta.json";

        //如存在meta文件，则在meta基础上修改
        if (FileUtil.exist(metaOutputPath)) {
            Meta oldMeta = JSONUtil.toBean(FileUtil.readUtf8String(metaOutputPath), Meta.class);
            BeanUtil.copyProperties(meta, oldMeta, CopyOptions.create().ignoreNullValue());
            meta = oldMeta;

            //追加配置参数
            List<Meta.FileConfig.FileInfo> fileInfos = meta.getFileConfig().getFiles();
            fileInfos.addAll(fileInfoList);
            List<Meta.ModelConfig.ModelInfo> modelInfos = meta.getModelConfig().getModels();
            modelInfos.addAll(newModelInfoList);

            //去重
            meta.getFileConfig().setFiles(distinctFile(fileInfos));
            meta.getModelConfig().setModels(distinctModel(modelInfos));
        } else {
            Meta.FileConfig fileConfig = new Meta.FileConfig();
            meta.setFileConfig(fileConfig);
            fileConfig.setSourceRootPath(sourceRootPath);
            List<Meta.FileConfig.FileInfo> fileInfos = new ArrayList<>();
            fileConfig.setFiles(fileInfos);
            fileInfos.addAll(fileInfoList);

            Meta.ModelConfig modelConfig = new Meta.ModelConfig();
            meta.setModelConfig(modelConfig);
            ArrayList<Meta.ModelConfig.ModelInfo> modelInfos = new ArrayList<>();
            modelConfig.setModels(modelInfos);
            modelInfos.addAll(newModelInfoList);
        }

        //输出分组内和分组外的去重设置
        if (null != templateMakerOutputConfig){
            if (templateMakerOutputConfig.isRemoveGroupFilesFromRoot()){
                List<Meta.FileConfig.FileInfo> infoList = meta.getFileConfig().getFiles();
                meta.getFileConfig().setFiles(TemplateUtils.removeGroupFilesFromRoot(infoList));
            }
        }

        //输出元数据文件
        FileUtil.writeUtf8String(JSONUtil.toJsonPrettyStr(meta), metaOutputPath);
        return id;
    }

    /**
     * 获取多个模型数据
     *
     * @param templateMakerModelConfig
     * @return
     */
    private static ArrayList<Meta.ModelConfig.ModelInfo> getModelInfoList(TemplateMakerModelConfig templateMakerModelConfig) {
        //本次新增的模型配置列表
        ArrayList<Meta.ModelConfig.ModelInfo> newModelInfoList = new ArrayList<>();
        List<TemplateMakerModelConfig.ModelInfoConfig> models =
                templateMakerModelConfig.getModels();

        //空值校验
        if (null == templateMakerModelConfig){
            return newModelInfoList;
        }
        if (CollUtil.isEmpty(models)){
            return newModelInfoList;
        }

        //处理文件模型信息
        //转换为可配置可接受的ModelInfo对象
        List<Meta.ModelConfig.ModelInfo> inputModelInfoList = models.stream().map(modelInfoConfig -> {
            Meta.ModelConfig.ModelInfo modelInfo = new Meta.ModelConfig.ModelInfo();
            BeanUtil.copyProperties(modelInfoConfig, modelInfo);
            return modelInfo;
        }).collect(Collectors.toList());

        //处理模型组
        TemplateMakerModelConfig.ModelGroupConfig modelGroupConfig =
                templateMakerModelConfig.getModelGroupConfig();
        if (null != modelGroupConfig) {
            //复制变量
            Meta.ModelConfig.ModelInfo modelGroupInfo = new Meta.ModelConfig.ModelInfo();
            BeanUtil.copyProperties(modelGroupConfig, modelGroupInfo);

//            String condition = modelGroupConfig.getCondition();
//            String groupKey = modelGroupConfig.getGroupKey();
//            String groupName = modelGroupConfig.getGroupName();
//            modelGroupInfo.setGroupKey(groupKey);
//            modelGroupInfo.setGroupName(groupName);
//            modelGroupInfo.setCondition(condition);

            //模型放入分组内
            modelGroupInfo.setModels(inputModelInfoList);
            newModelInfoList.add(modelGroupInfo);
        } else {
            newModelInfoList.addAll(inputModelInfoList);
        }
        return newModelInfoList;
    }

    /**
     * 生成多个文件
     *
     * @param templateMakerFileConfig
     * @param templateMakerModelConfig
     * @param sourceRootPath
     * @return
     */
    private static List<Meta.FileConfig.FileInfo> makerFileTemplates(
            TemplateMakerFileConfig templateMakerFileConfig, TemplateMakerModelConfig templateMakerModelConfig,
            String sourceRootPath) {
        //遍历输入文件
        List<Meta.FileConfig.FileInfo> fileInfoList = new ArrayList<>();

        //空值校验
        if (null == templateMakerFileConfig){
            return fileInfoList;
        }
        List<TemplateMakerFileConfig.FileInfoConfig> fileInfoConfigList = templateMakerFileConfig.getFiles();
        if (CollUtil.isEmpty(fileInfoConfigList)){
            return fileInfoList;
        }

        for (TemplateMakerFileConfig.FileInfoConfig fileInfoConfig : fileInfoConfigList) {
            String infoConfigPath = fileInfoConfig.getPath();

            //判断是否为先对路径，是则改为绝对路径
            if (!infoConfigPath.startsWith(sourceRootPath)) {
                infoConfigPath = sourceRootPath + File.separator + infoConfigPath;
            }

            //获取过滤后的文件列表
            List<File> files = FileFilter.doFilter(infoConfigPath, fileInfoConfig.getFilterConfigList());

            //不处理.ftl文件
            files = files.stream()
                    .filter(file -> !file.getAbsolutePath().endsWith(".ftl"))
                    .collect(Collectors.toList());
            for (File file : files) {
                Meta.FileConfig.FileInfo fileInfo =
                        makeFileTemplate(templateMakerModelConfig, sourceRootPath, file, fileInfoConfig);
                //放入
                fileInfoList.add(fileInfo);
            }
        }

        //处理文件组
        TemplateMakerFileConfig.FileGroupConfig fileGroupConfig =
                templateMakerFileConfig.getFileGroupConfig();
        if (null != fileGroupConfig) {
            String condition = fileGroupConfig.getCondition();
            String groupName = fileGroupConfig.getGroupName();
            String groupKey = fileGroupConfig.getGroupKey();

            //添加分组配置
            Meta.FileConfig.FileInfo fileGroupInfo = new Meta.FileConfig.FileInfo();
            fileGroupInfo.setType(FileTypeEnum.GROUP.getValue());
            fileGroupInfo.setCondition(condition);
            fileGroupInfo.setGroupKey(groupKey);
            fileGroupInfo.setFiles(fileInfoList);
            fileGroupInfo.setGroupName(groupName);

            fileInfoList = new ArrayList<>();
            fileInfoList.add(fileGroupInfo);
        }
        return fileInfoList;
    }

    /**
     * 制作模板文件
     *
     * @param templateMakerModelConfig
     * @param sourceRootPath
     * @param inputFile
     * @param fileInfoConfig
     * @return
     */
    private static Meta.FileConfig.FileInfo makeFileTemplate(
            TemplateMakerModelConfig templateMakerModelConfig,
            String sourceRootPath, File inputFile,
            TemplateMakerFileConfig.FileInfoConfig fileInfoConfig) {
        String fileInputAbsolutePath = inputFile.getAbsolutePath().replaceAll("\\\\", "/");
        String fileOutputAbsolutePath = fileInputAbsolutePath + ".ftl";

        //输入路径转为相对路径
        String fileInputPath = fileInputAbsolutePath.replace(sourceRootPath + "/", "");
        String fileOutputPath = fileInputPath + ".ftl";

        //生成模板文件
        String fileContent;
        boolean existTemplateFile = FileUtil.exist(fileOutputAbsolutePath);

        //模板存在则在基础上继续，防止重复
        if (existTemplateFile) {
            fileContent = FileUtil.readUtf8String(fileOutputAbsolutePath);
        } else {
            fileContent = FileUtil.readUtf8String(fileInputAbsolutePath);
        }

        //遍历循环模型进行多次替换
        TemplateMakerModelConfig.ModelGroupConfig modelGroupConfig = templateMakerModelConfig.getModelGroupConfig();
        String newFileContent = fileContent;
        String replacement;
        for (TemplateMakerModelConfig.ModelInfoConfig modelConfigModel : templateMakerModelConfig.getModels()) {
            //判断是否为分组
            if (null == modelGroupConfig) {
                replacement = String.format("${%s}", modelConfigModel.getFieldName());
            } else {
                String groupKey = modelGroupConfig.getGroupKey();
                replacement = String.format("${%s.%s}", groupKey, modelConfigModel.getFieldName());
            }
            //实现多次替换
            newFileContent = StrUtil.replace(newFileContent, modelConfigModel.getReplaceText(), replacement);
        }

        //文件配置信息
        Meta.FileConfig.FileInfo fileInfo = new Meta.FileConfig.FileInfo();
        fileInfo.setType(FileTypeEnum.FILE.getValue());
        fileInfo.setOutputPath(fileInputPath);
        fileInfo.setInputPath(fileOutputPath);
        fileInfo.setCondition(fileInfoConfig.getCondition());
        fileInfo.setGenerateType(FileGenerateTypeEnum.STATIC.getValue());

        //是否发生修改
        boolean contentEquals = newFileContent.equals(fileContent);
        //和源文件一致，则为静态（有bug后面想办法改） todo
        if (!existTemplateFile) {
            if (contentEquals) {
                fileInfo.setInputPath(fileInputPath);
                fileInfo.setGenerateType(FileGenerateTypeEnum.STATIC.getValue());
            } else {
                //生成模板文件
                FileUtil.writeUtf8String(newFileContent, fileOutputAbsolutePath);
            }
        } else if (!contentEquals) {
            //更新模板文件
            FileUtil.writeUtf8String(newFileContent, fileOutputAbsolutePath);
        }

        return fileInfo;
    }

    /**
     * 使用hashmap模型去重
     *
     * @param modelInfos
     * @return
     */
    private static List<Meta.ModelConfig.ModelInfo> distinctModel(
            List<Meta.ModelConfig.ModelInfo> modelInfos) {
        //同组合并，不同组保留
        //有分组，以组为单位划分
        Map<String, List<Meta.ModelConfig.ModelInfo>> groupKeyModelInfoMap = modelInfos.stream()
                .filter(modelInfo -> StrUtil.isNotBlank(modelInfo.getGroupKey()))
                .collect(Collectors.groupingBy(Meta.ModelConfig.ModelInfo::getGroupKey));
        //同组内模型配置合并
        Map<String, Meta.ModelConfig.ModelInfo> groupKeyMergedModelInfoMap = new HashMap<>();
        for (Map.Entry<String, List<Meta.ModelConfig.ModelInfo>> entry : groupKeyModelInfoMap.entrySet()) {
            List<Meta.ModelConfig.ModelInfo> modelInfoList = entry.getValue();
            List<Meta.ModelConfig.ModelInfo> newModelInfoList =
                    new ArrayList<>(modelInfoList.stream()
                            .flatMap(modelInfo -> modelInfo.getModels().stream())
                            .collect(Collectors.toMap(
                                    Meta.ModelConfig.ModelInfo::getFieldName, o -> o, (exist, replace) -> replace))
                            .values());

            //使用新的group配置
            Meta.ModelConfig.ModelInfo newModelInfo = CollUtil.getLast(modelInfoList);
            newModelInfo.setModels(newModelInfoList);
            String groupKey = entry.getKey();
            groupKeyMergedModelInfoMap.put(groupKey, newModelInfo);
        }

        //将模型分组添加到结果列表
        ArrayList<Meta.ModelConfig.ModelInfo> resList =
                new ArrayList<>(groupKeyMergedModelInfoMap.values());

        //将未分组的模型添加到结果列表
        List<Meta.ModelConfig.ModelInfo> noGroupModelInfoList = modelInfos.stream()
                .filter(modelInfo -> StrUtil.isBlank(modelInfo.getGroupKey()))
                .collect(Collectors.toList());
        resList.addAll(noGroupModelInfoList.stream()
                .collect(Collectors.toMap(
                        Meta.ModelConfig.ModelInfo::getFieldName, o -> o, (exist, replace) -> replace))
                .values());
        return resList;
    }

    /**
     * 使用hashmap文件去重
     *
     * @param fileInfos
     * @return
     */
    private static List<Meta.FileConfig.FileInfo> distinctFile(
            List<Meta.FileConfig.FileInfo> fileInfos) {
        //同组合并，不同组保留
        //有分组，以组为单位划分
        Map<String, List<Meta.FileConfig.FileInfo>> groupKeyFileInfoMap = fileInfos.stream()
                .filter(fileInfo -> StrUtil.isNotBlank(fileInfo.getGroupKey()))
                .collect(Collectors.groupingBy(Meta.FileConfig.FileInfo::getGroupKey));
        //同组内模型配置合并
        Map<String, Meta.FileConfig.FileInfo> groupKeyMergedFileInfoMap = new HashMap<>();
        for (Map.Entry<String, List<Meta.FileConfig.FileInfo>> entry : groupKeyFileInfoMap.entrySet()) {
            List<Meta.FileConfig.FileInfo> fileInfoList = entry.getValue();
            List<Meta.FileConfig.FileInfo> newFileInfoList =
                    new ArrayList<>(fileInfoList.stream()
                            .flatMap(fileInfo -> fileInfo.getFiles().stream())
                            .collect(Collectors.toMap(
                                    Meta.FileConfig.FileInfo::getOutputPath, o -> o, (exist, replace) -> replace))
                            .values());

            //使用新的group配置
            Meta.FileConfig.FileInfo newFileInfo = CollUtil.getLast(fileInfoList);
            newFileInfo.setFiles(newFileInfoList);
            String groupKey = entry.getKey();
            groupKeyMergedFileInfoMap.put(groupKey, newFileInfo);
        }

        //将模型分组添加到结果列表
        ArrayList<Meta.FileConfig.FileInfo> resList =
                new ArrayList<>(groupKeyMergedFileInfoMap.values());

        //将未分组的模型添加到结果列表
        List<Meta.FileConfig.FileInfo> noGroupFileInfoList = fileInfos.stream()
                .filter(modelInfo -> StrUtil.isBlank(modelInfo.getGroupKey()))
                .collect(Collectors.toList());

        resList.addAll(noGroupFileInfoList.stream()
                .collect(Collectors.toMap(
                        Meta.FileConfig.FileInfo::getOutputPath, o -> o, (exist, replace) -> replace))
                .values());
        return resList;
    }

}
